import * as fs from 'fs'
import * as dotenv from 'dotenv'
import * as Joi from "joi";

export interface EnvConfig {
    [prop: string]: string;
}


/**
 * 配置文件服务
 * 参考：https://www.jianshu.com/p/12d80a125291
 */
export class ConfigService {
    // 配置文件
    private readonly envConfig: EnvConfig;
    // 配置文件(模板)
    private readonly configTemp: EnvConfig = {
        // 当前环境
        NODE_ENV: Joi.string()
            .valid(['development', 'production', 'test', 'provision'])
            .default('development'),

        // 端口
        PORT: Joi.string().default(3000),

        // 数据库类型
        DATABASE_TYPE: Joi.string().default('mysql'),

        // 数据库域名
        DATABASE_HOST: Joi.string().default('localhost'),

        // 数据库端口
        DATABASE_PORT: Joi.number().default(3306),

        // 数据库用户名
        DATABASE_USER: Joi.string().default('root'),

        // 数据库密码
        DATABASE_PASSWORD: Joi.string(),

        // 数据库库名
        DATABASE_DB: Joi.string().required(),

        // 数据库是否同步
        DATABASE_SYNCHRONIZE: Joi.boolean().default(false),

        // 是否开启数据签名
        IS_USE_SIGN: Joi.boolean().default(false),

        // 签名过期时间
        SIGN_EXPIRATION_TIME: Joi.number().default(30),

        // API路有前缀
        API_ROUTER_PREFIX: Joi.string(),

        // 文件上传路径
        UPLOAD_DIR: Joi.string().required()
    }

    constructor(env: string) {
        const envFilePath = `env/${env}.env`
        const config: EnvConfig = dotenv.parse(fs.readFileSync(envFilePath))
        this.envConfig = this.validateInput(config)
        return this.proxyConfig(this.envConfig) // 代理envConfig, 外部可直接访问 configService[prop]
    }

    /**
     * 验证写入参数
     * @param envConfig
     * @private
     * @return {object}
     */
    private validateInput(envConfig: EnvConfig): EnvConfig {
        const envVarsScheme: Joi.ObjectSchema = Joi.object(this.configTemp)
        const { err, value: validatedEnvConfig } = Joi.validate(envConfig, envVarsScheme)
        if (err) {
            throw new Error(err.message)
        }
        return validatedEnvConfig
    }

    /**
     * 代理envConfig, 外部可直接访问 configService[prop]
     * @param {EnvConfig} config
     * @private
     * @return {Proxy}
     */
    private proxyConfig(config: EnvConfig) {
        const context = this
        return new Proxy(context, {
            get(target, prop: string, receiver: any): any {
                return config[prop]
            }
        })
    }
}
